//-------------------------------------------------------------------------------------------------
// Copyright (c) Bradford W. Mott and Flare Contributors
// North Carolina State University, Department of Computer Science
// The IntelliMedia Group
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//-------------------------------------------------------------------------------------------------

using System.Collections.Generic;
using UnityEngine;
using Flare.Display;
using Flare.Geom;

namespace Flare.Text
{
    /// <summary>
    /// The EmbeddedFont class represents a font embedded in a SWF file. Generally,
    /// device fonts should be used since they provide improved performance; however,
    /// if a font mapping hasn't been specified then the EmbeddedFont can be used
    /// for rendering.
    /// </summary>
    public class EmbeddedFont : Flare.Text.Font
    {
        internal const float baseline = 225.0f;

        internal int languageCode { get; set; }

        internal Dictionary<uint, char> codeTable { get; set; }

        /// <summary>
        /// Returns true if the embedded font has layout metrics.
        /// </summary>
        public bool hasLayout { get; 
            /* \cond */ internal set; /* \endcond */ }

        /// <summary>
        /// Returns the ascent of the embedded font, which is the distance from the
        /// baseline to the top of the line.
        /// </summary>
        public float ascent { get; 
            /* \cond */ internal set; /* \endcond */ }

        /// <summary>
        /// Returns the descent of the embedded font, which is the distance from the
        /// baseline to the bottom of the line.
        /// </summary>
        public float descent { get; 
            /* \cond */ internal set; /* \endcond */ }

        /// <summary>
        /// Returns the leading of the embedded font, which is the vertical distance
        /// between lines of text.
        /// </summary>
        public float leading { get; 
            /* \cond */ internal set; /* \endcond */ }

        internal float[] advanceTable { get; set; }

        internal Rectangle[] boundsTable { get; set; }

        internal KerningRecord[] kerningRecords { get; set; }

        internal Shape[] glyphShapes { get; set; }

        private Dictionary<uint, Texture2D> textures { get; set; }

        internal EmbeddedFont(string fontName, string fontStyle)
            : base(fontName, fontStyle)
        {
            this.textures = new Dictionary<uint, Texture2D>();
        }

        internal Texture2D GetGlyphTexture(uint index)
        {
            if (Application.HasProLicense())
            {
                if (!this.textures.ContainsKey(index))
                {
                    CreateGlyphTexture(index);
                }
    
                return this.textures[index];
            }
            else
            {
                return null;
            }
        }

        public override string ToString()
        {
            return string.Format("[EmbeddedFont: name={0}]", fontName);
        }

        protected void CreateGlyphTexture(uint index)
        {
            float scale = 1.0f / 4.0f;
            RenderTexture rtex = RenderTexture.GetTemporary(
                (int)(1024 * scale), (int)(1024 * scale), 0, RenderTextureFormat.ARGB32);
            rtex.useMipMap = false;
            rtex.filterMode = FilterMode.Point;
            rtex.wrapMode = TextureWrapMode.Clamp;

            // Set up projection matrix and viewport for rendering
            GL.PushMatrix();
            GL.LoadPixelMatrix(0, rtex.width - 1, rtex.height - 1, 0);
            GL.Viewport(new Rect(0.0f, 0.0f, rtex.width, rtex.height));

            // Use rtex for rendering
            RenderTexture.active = rtex;

            Shape glyph = this.glyphShapes[index];
            GL.Clear(true, true, Color.clear);

            Matrix mat = new Matrix(scale, 0.0f, 0.0f, scale, 0, (1024 - baseline) * scale);
            ColorTransform ct = new ColorTransform(0.0f, 0.0f, 0.0f, 0.0f,
                255.0f, 255.0f, 255.0f, 255.0f);
            glyph.graphics.Render(mat, ct);

            Texture2D tex2D = new Texture2D(rtex.width, rtex.height,
                TextureFormat.ARGB32, false);
            tex2D.filterMode = FilterMode.Point;
            tex2D.wrapMode = TextureWrapMode.Clamp;
            tex2D.ReadPixels(new Rect(0, 0, rtex.width, rtex.height), 0, 0, false);

            RenderTexture.active = null;
            RenderTexture.ReleaseTemporary(rtex);

            // Restore rendering state
            GL.PopMatrix();

            Color32[] colors = tex2D.GetPixels32();
            Texture2D alphatex = new Texture2D(tex2D.width, tex2D.height,
                TextureFormat.Alpha8, true);
            alphatex.filterMode = FilterMode.Trilinear;
            alphatex.wrapMode = TextureWrapMode.Clamp;
            alphatex.SetPixels32(colors);
            alphatex.Apply(true, true);
            this.textures.Add(index, alphatex);
        }

        public class KerningRecord
        {
            public char code1 { get; private set; }
            public char code2 { get; private set; }
            public float adjustment { get; private set; }

            public KerningRecord(char code1, char code2, float adjustment)
            {
                this.code1 = code1;
                this.code2 = code2;
                this.adjustment = adjustment;
            }
        }
    }
}